#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <sys/select.h>
#include "sock.hpp"
#include "log.hpp"
#include "err.hpp"

const static int gport = 8888;

typedef int type_t;


class SelectServer{

    static const int N = (sizeof(fd_set) * 8);

public:
    SelectServer(uint16_t port = gport):_port(port){

    }
    void Init(){
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N;i++){
            _fdarr[i] = defaultfd;
        }
    }

    void Accepter(){
        // cout << "有一个新链接到来" << endl;

        string clientip;
        uint16_t clinetport;
        int sock = _listensock.Accept(&clientip, &clinetport);
        if(sock < 0){
            return;
        }

        //得到了对应的sock，不能在这里进行read/recv
        //因为上次select只知道有新链接，而不知道新链接的读写fd是否就绪
        //如果在这里进行读写，又会变成阻塞式
        //所以需要将sock交给select，等待读写fd就绪
        logMessage(DEBUG, "%s:%d, sock:%d", clientip.c_str(), clinetport, sock);
        //要让select进行管理，只需要把sock添加到_fdarr就行
        for (int i = 1; i < N; i++){
            if(_fdarr[i]==defaultfd){
                _fdarr[i] = sock;
                break;
            }
            //当数组满了
            if(i>=N-1){
                close(sock);
                logMessage(WARNING, "_fdarr full");
            }
        }
    }

    void HandlerEvent(fd_set &rfds){
        for (int i = 0; i < N; i++){
            // 对select的结果进行检测
            if(_fdarr[i] == defaultfd){
                continue;
            }

            //当就绪的fd是监听套接字 且 监听套接字在rfds中，进行链接
            if (_fdarr[i] == _listensock.Fd() && FD_ISSET(_listensock.Fd(), &rfds)){
                Accepter();
            }//如果就绪的fd是在rfds中，但不是监听套接字，进行处理
            else if(_fdarr[i] != _listensock.Fd() && FD_ISSET(_fdarr[i], &rfds)){
                //ServiceIO()
                //这里的读取是有问题的，不能保证读取到一个完整的报文，需要配合协议
                char buf[1024];
                ssize_t s = recv(_fdarr[i], buf, sizeof(buf) - 1, 0);   //这里的读取不会被阻塞，因为已经检测到就绪
                if(s > 0){
                    buf[s-1] = 0;
                    cout << "client# " << buf << endl;

                    //回传数据也要用select管理
                    string echo = buf;
                    echo += "[echo]";
                    send(_fdarr[i], echo.c_str(), echo.size(), 0);
                }
                else if (s == 0){
                    close(_fdarr[i]);
                    logMessage(INFO, "client quit! -- fd:%d", _fdarr[i]);
                    _fdarr[i] = defaultfd;
                }
                else{
                    close(_fdarr[i]);
                    logMessage(WARNING, "recv error, client quit! -- fd:%d", _fdarr[i]);
                    _fdarr[i] = defaultfd;
                }
            }
        }
        //接收
    }

    void Start(){
        //在网络中，新链接的到来被当做读事件就绪

        //将监听套接字添加到fd数组中
        _fdarr[0] = _listensock.Fd();
        while (1)
        {
            // struct timeval timeout = {2, 0};
            // 因为rfds是输入输出型参数，会被更改，所以输入前需要对rfds进行记录
            // 因为服务器运行是sockfd的值一直在动态变化，所以maxfd也一定在变化 

            //创建读fd集合
            fd_set rfds;
            //清空集合
            FD_ZERO(&rfds);
            // //将_listensock添加到读fd集合
            // FD_SET(_listensock.Fd(), &rfds);
            //将所有fd添加到rfds
            int maxfd = _fdarr[0];
            for (int i = 0; i < N; i++){
                if(_fdarr[i]==defaultfd){
                    continue;
                }
                FD_SET(_fdarr[i], &rfds);
                if(maxfd<_fdarr[i]){
                    maxfd = _fdarr[i];
                }
            }

            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n){
                case 0:
                    logMessage(DEBUG, "time out, %d:%s", errno, strerror(errno));
                    break;
                case -1:
                    logMessage(WARNING, "%d:%s", errno, strerror(errno));
                    break;
                default:
                    logMessage(DEBUG, "有一个就绪事件发生:%d", n);
                    HandlerEvent(rfds);
                    DebugPrint();
                    break;
            }
        }
    }

    void DebugPrint(){
        cout << "_fdarr[]: ";
        for (int i = 0; i < N;i++){
            if(_fdarr[i]!=defaultfd){
                cout << _fdarr[i] << " ";
            }
        }
        cout << endl;
    }

    ~SelectServer(){
        _listensock.Close();
    }

private:
    uint16_t _port;
    Sock _listensock;
    type_t _fdarr[N];
};
